//
//  WJJCalendarView.m
//  WJJCalendarView
//
//  Created by JunWang on 16/3/7.
//  Copyright (c) 2016年 Insigma Hengtian Software Co.,Ltd  All rights reserved.
//




#import "WJJCalendarDayView.h"
#import "WJJCalendarMonthSelectorView.h"
#import "WJJCalendarMonthView.h"
#import "WJJCalendarView.h"
#import "WJJCalendarDayView.h"
#import "UIView+WJJUIViewExt.h"
#import "UIColor+ExtUIColor.h"
#import "WJJCalendarLogic.h"


@interface WJJCalendarView ()


@property (nonatomic, copy) NSDateComponents *draggingFixedDay;
@property (nonatomic, copy) NSDateComponents *draggingStartDay;
@property (nonatomic, assign) BOOL draggedOffStartDay;

@property (nonatomic, strong) NSMutableDictionary *monthViews;
@property (nonatomic, strong) UIView *monthContainerView;
@property (nonatomic, strong) UIView *monthContainerViewContentView;
@property (nonatomic, strong) WJJCalendarMonthSelectorView *monthSelectorView;


@end


@implementation WJJCalendarView {
    CGFloat _dayViewHeight;
    NSDateComponents *_visibleMonth;
}


#pragma mark - Memory management

- (void)dealloc {
}


#pragma mark - Initialisation

// Designated initialisers

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self != nil) {
        [self commonInit];
    }
    
    return self;
}

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self != nil) {
        [self commonInit];
    }

    return self;
}

- (void)commonInit {
    _dayViewHeight = 54;
    _visibleMonth = [[NSCalendar currentCalendar] components:NSCalendarUnitYear | NSCalendarUnitMonth  | NSCalendarUnitDay | NSCalendarUnitWeekday | NSCalendarUnitCalendar fromDate:[NSDate date]];
    _visibleMonth.day = 1;
    //设置月历上标题栏
    self.monthSelectorView = [[[self class] monthSelectorViewClass] view];
    self.monthSelectorView.width = self.width;
    self.monthSelectorView.backgroundColor = [UIColor clearColor];
    self.monthSelectorView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
    [self addSubview:self.monthSelectorView];
    [self.monthSelectorView.backButton addTarget:self action:@selector(didTapMonthBack:) forControlEvents:UIControlEventTouchUpInside];
    [self.monthSelectorView.forwardButton addTarget:self action:@selector(didTapMonthForward:) forControlEvents:UIControlEventTouchUpInside];

    // Month views are contained in a content view inside a container view - like a scroll view, but not a scroll view so we can have proper control over animations
    CGRect frame = self.bounds;
    frame.origin.x = 0;
    frame.origin.y = CGRectGetMaxY(self.monthSelectorView.frame);
    frame.size.height -= frame.origin.y;
    self.monthContainerView = [[UIView alloc] initWithFrame:frame];
    self.monthContainerView.clipsToBounds = YES;
    [self addSubview:self.monthContainerView];
    
    self.monthContainerViewContentView = [[UIView alloc] initWithFrame:self.monthContainerView.bounds];
    [self.monthContainerView addSubview:self.monthContainerViewContentView];
    
    self.monthViews = [[NSMutableDictionary alloc] init];

    [self updateMonthLabelMonth:_visibleMonth];
    [self positionViewsForMonth:_visibleMonth fromMonth:_visibleMonth animated:NO];
    
}


#pragma mark - Properties

+ (Class)monthSelectorViewClass {
    return [WJJCalendarMonthSelectorView class];
}

+ (Class)monthViewClass {
    return [WJJCalendarMonthView class];
}

+ (Class)dayViewClass {
    return [WJJCalendarDayView class];
}

#pragma mark - Setter

- (void)setSelectedRange:(WJJCalendarRange *)selectedRange {
    _selectedRange = selectedRange;
    
    for (WJJCalendarMonthView *monthView in self.monthViews.allValues) {
        [monthView updateDaySelectionStatesForRange:self.selectedRange];
    }
}

- (void)setDraggingStartDay:(NSDateComponents *)draggingStartDay {
    _draggingStartDay = [draggingStartDay copy];
    if (draggingStartDay == nil) {
//        [self.dayCalloutView removeFromSuperview];
    }
}

- (void)setVisibleMonth:(NSDateComponents *)visibleMonth {
    [self setVisibleMonth:visibleMonth animated:NO];
}
//设置显示的月份日期
- (void)setVisibleMonth:(NSDateComponents *)visibleMonth animated:(BOOL)animated {
    NSDateComponents *fromMonth = [_visibleMonth copy];
    _visibleMonth = [visibleMonth.date dslCalendarView_monthWithCalendar:self.visibleMonth.calendar];
    
    [self updateMonthLabelMonth:_visibleMonth];//更新月份标题
    [self positionViewsForMonth:_visibleMonth fromMonth:fromMonth animated:animated];//滚动日历
    
}

#pragma mark - Getter

- (NSDateComponents*)visibleMonth {
    return [_visibleMonth copy];
}

#pragma mark - Events

//向后翻页
- (void)didTapMonthBack:(id)sender {
    NSDateComponents *newMonth = self.visibleMonth;
    newMonth.month--;
    [self setVisibleMonth:newMonth animated:YES];
}

//向前翻页
- (void)didTapMonthForward:(id)sender {
    NSDateComponents *newMonth = self.visibleMonth;
    newMonth.month++;
    [self setVisibleMonth:newMonth animated:YES];
}


#pragma mark -  UpdateView

- (void)updateMonthLabelMonth:(NSDateComponents*)month {
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"MMMM yyyy";
    NSDate *date = [month.calendar dateFromComponents:month];
    //设置日历标题字体格式
    NSMutableString *dateStr = [NSMutableString stringWithString:[formatter stringFromDate:date]];
    [dateStr insertString:@"/" atIndex:[dateStr length] - 4];
    NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc]initWithString:dateStr];
    [attributedStr addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:213.0/255 green:213.0/255 blue:213.0/255 alpha:1] range:NSMakeRange([dateStr length] - 5, 5)];
    [attributedStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:14] range:NSMakeRange([dateStr length] - 5, 5)];
    [attributedStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:17] range:NSMakeRange(0, [dateStr length] - 5)];
    self.monthSelectorView.titleLabel.attributedText = attributedStr;
    
}

- (NSString*)monthViewKeyForMonth:(NSDateComponents*)month {
    month = [month.calendar components:NSCalendarUnitYear | NSCalendarUnitMonth fromDate:month.date];
    return [NSString stringWithFormat:@"%ld.%ld", (long)month.year, (long)month.month];
}

- (WJJCalendarMonthView*)cachedOrCreatedMonthViewForMonth:(NSDateComponents*)month {
    month = [month.calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday | NSCalendarUnitCalendar fromDate:month.date];
    NSString *monthViewKey = [self monthViewKeyForMonth:month];
    WJJCalendarMonthView *monthView = [self.monthViews objectForKey:monthViewKey];
    if (monthView == nil) {
        monthView = [[[[self class] monthViewClass] alloc] initWithMonth:month width:self.bounds.size.width dayViewClass:[[self class] dayViewClass] dayViewHeight:_dayViewHeight];
        [self.monthViews setObject:monthView forKey:monthViewKey];
        [self.monthContainerViewContentView addSubview:monthView];
        [monthView updateDaySelectionStatesForRange:self.selectedRange];
    }
    return monthView;
}


/**
 * 变动日历显示
 *
 * @param:  month为需显示的月份，fromMonth为原来的月份，animated为动画指示字符
 *
 * @return: nil
 */
- (void)positionViewsForMonth:(NSDateComponents*)month fromMonth:(NSDateComponents*)fromMonth animated:(BOOL)animated
{
    fromMonth = [fromMonth copy];//上个月
    month = [month copy];//这个月
    
    CGFloat nextVerticalPosition = 0;
    CGFloat startingVerticalPostion = 0;
    CGFloat restingVerticalPosition = 0;
    CGFloat restingHeight = 0;
    
    NSComparisonResult monthComparisonResult = [month.date compare:fromMonth.date];
    //设置动画时间 月份不变与月份有变化区分
    NSTimeInterval animationDuration = (monthComparisonResult == NSOrderedSame || !animated) ? 0.0 : 0.5;
    NSMutableArray *activeMonthViews = [[NSMutableArray alloc] init];
    //第次生成前两个月与后两个月，共五个月的monthView
    for (NSInteger monthOffset = -2; monthOffset <= 2; monthOffset += 1) {
        NSDateComponents *offsetMonth = [month copy];//下一个月的月份
        offsetMonth.month = offsetMonth.month + monthOffset;
        offsetMonth = [offsetMonth.calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday | NSCalendarUnitCalendar fromDate:offsetMonth.date];
        // 检查当前月份是不是会与前个月重叠
        if (![WJJCalendarLogic isNoOverlappedWithlastMonth:offsetMonth]) {
            nextVerticalPosition -= _dayViewHeight;
        }
        // Create and position the month view
        WJJCalendarMonthView *monthView = [self cachedOrCreatedMonthViewForMonth:offsetMonth];
        [activeMonthViews addObject:monthView];
        [monthView.superview bringSubviewToFront:monthView];
        CGRect frame = monthView.frame;
        frame.origin.y = nextVerticalPosition;
        nextVerticalPosition += frame.size.height;
        monthView.frame = frame;

        // Check if this view is where we should animate to or from
        if (monthOffset == 0) {
            // This is the target month so we can use it to determine where to scroll to
            restingVerticalPosition = monthView.frame.origin.y;
            restingHeight += monthView.bounds.size.height;
        }
        else if (monthOffset == 1 && monthComparisonResult == NSOrderedAscending) {
            // 向后翻一月
            startingVerticalPostion = monthView.frame.origin.y;
            if ([WJJCalendarLogic isNoOverlappedWithlastMonth:offsetMonth]) {
                startingVerticalPostion -= _dayViewHeight;
            }
        }
        else if (monthOffset == -1 && monthComparisonResult == NSOrderedDescending) {
            // 向前翻一月
            startingVerticalPostion = monthView.frame.origin.y;
            if ([WJJCalendarLogic isNoOverlappedWithlastMonth:offsetMonth]) {
                startingVerticalPostion -= _dayViewHeight;
            }
        }
    }
    //适配monthViewContainer的大小
    CGRect frame = self.monthContainerViewContentView.frame;
    frame.size.height = CGRectGetMaxY([[activeMonthViews lastObject] frame]);
    self.monthContainerViewContentView.frame = frame;
    //移除旧的月视图
    NSArray *monthViewKeyes = self.monthViews.allKeys;
    for (NSString *key in monthViewKeyes) {
        UIView *monthView = [self.monthViews objectForKey:key];
        if (![activeMonthViews containsObject:monthView]) {
            [monthView removeFromSuperview];
            [self.monthViews removeObjectForKey:key];
        }
    }
    
    // Position the content view to show where we're animating from
    if (monthComparisonResult != NSOrderedSame) {
        CGRect frame = self.monthContainerViewContentView.frame;
        frame.origin.y = -startingVerticalPostion;
        self.monthContainerViewContentView.frame = frame;
    }
    
    self.userInteractionEnabled = NO;
    [UIView animateWithDuration:animationDuration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        for (NSInteger index = 0; index < activeMonthViews.count; index++) {
            WJJCalendarMonthView *monthView = [activeMonthViews objectAtIndex:index];
             for (WJJCalendarDayView *dayView in monthView.dayViews) {
                 // Use a transition so it fades between states nicely
                 [UIView transitionWithView:dayView duration:animationDuration options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
                     dayView.inCurrentMonth = (index == 2);
                 } completion:NULL];
             }
        }
        
        // Animate the content view to show the target month
        CGRect frame = self.monthContainerViewContentView.frame;
        frame.origin.y = -restingVerticalPosition;
        self.monthContainerViewContentView.frame = frame;
        // Resize the container view to show the height of the target month
        frame = self.monthContainerView.frame;
        frame.size.height = restingHeight;
        self.monthContainerView.frame = frame;
        frame = self.frame;
        frame.size.height = CGRectGetMaxY(self.monthContainerView.frame);
        self.frame = frame;
        // Tell the delegate method that we're about to animate to a new month
        if (monthComparisonResult != NSOrderedSame && [self.delegate respondsToSelector:@selector(calendarView:willChangeToVisibleMonth:duration:)]) {
            [self.delegate calendarView:self willChangeToVisibleMonth:[month copy] duration:animationDuration];
        }
    } completion:^(BOOL finished) {
        self.userInteractionEnabled = YES;
        if (finished) {
            // Tell the delegate method that we've animated to a new month
            if (monthComparisonResult != NSOrderedSame && [self.delegate respondsToSelector:@selector(calendarView:didChangeToVisibleMonth:)]) {
                [self.delegate calendarView:self didChangeToVisibleMonth:[month copy]];
            }
        }
    }];
}


#pragma mark - Touches

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    //得到点击的dayView
    WJJCalendarDayView *touchedView = [self dayViewForTouches:touches];
    if (touchedView == nil)
    {
        self.draggingStartDay = nil;
        return;
    }
    self.draggingStartDay = touchedView.day;
    self.draggingFixedDay = touchedView.day;
    self.draggedOffStartDay = NO;
    WJJCalendarRange *newRange = self.selectedRange;
    if (self.selectedRange == nil) {
        newRange = [[WJJCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
    }
    else if (![self.selectedRange.startDay isEqual:touchedView.day] && ![self.selectedRange.endDay isEqual:touchedView.day]) {
        newRange = [[WJJCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
    }
    else if ([self.selectedRange.startDay isEqual:touchedView.day])
    {
        self.draggingFixedDay = self.selectedRange.endDay;
    }
    else
    {
        self.draggingFixedDay = self.selectedRange.startDay;
    }
    
    if ([self.delegate respondsToSelector:@selector(calendarView:didDragToDay:selectingRange:)])
    {
        newRange = [self.delegate calendarView:self didDragToDay:touchedView.day selectingRange:newRange];
    }
    self.selectedRange = newRange;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    if (self.draggingStartDay == nil) {
        return;
    }
    
    WJJCalendarDayView *touchedView = [self dayViewForTouches:touches];
    if (touchedView == nil) {
        self.draggingStartDay = nil;
        return;
    }
    
    WJJCalendarRange *newRange;
    if ([touchedView.day.date compare:self.draggingFixedDay.date] == NSOrderedAscending) {
        newRange = [[WJJCalendarRange alloc] initWithStartDay:touchedView.day endDay:self.draggingFixedDay];
    }
    else {
        newRange = [[WJJCalendarRange alloc] initWithStartDay:self.draggingFixedDay endDay:touchedView.day];
    }

    if ([self.delegate respondsToSelector:@selector(calendarView:didDragToDay:selectingRange:)]) {
        newRange = [self.delegate calendarView:self didDragToDay:touchedView.day selectingRange:newRange];
    }
    self.selectedRange = newRange;

    if (!self.draggedOffStartDay) {
        if (![self.draggingStartDay isEqual:touchedView.day]) {
            self.draggedOffStartDay = YES;
        }
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if (self.draggingStartDay == nil) {
        return;
    }
    
    WJJCalendarDayView *touchedView = [self dayViewForTouches:touches];
    if (touchedView == nil) {
        self.draggingStartDay = nil;
        return;
    }
    
    if (!self.draggedOffStartDay && [self.draggingStartDay isEqual:touchedView.day]) {
        self.selectedRange = [[WJJCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
    }
    
    self.draggingStartDay = nil;
    
    // Check if the user has dragged to a day in an adjacent month
    if (touchedView.day.year != _visibleMonth.year || touchedView.day.month != _visibleMonth.month) {
        // Ask the delegate if it's OK to animate to the adjacent month
        BOOL animateToAdjacentMonth = YES;
        if ([self.delegate respondsToSelector:@selector(calendarView:shouldAnimateDragToMonth:)]) {
            animateToAdjacentMonth = [self.delegate calendarView:self shouldAnimateDragToMonth:[touchedView.dayAsDate dslCalendarView_monthWithCalendar:_visibleMonth.calendar]];
        }
        
        if (animateToAdjacentMonth) {
            if ([touchedView.dayAsDate compare:_visibleMonth.date] == NSOrderedAscending) {
                [self didTapMonthBack:nil];
            }
            else {
                [self didTapMonthForward:nil];
            }
        }
    }
    
    if ([self.delegate respondsToSelector:@selector(calendarView:didSelectRange:)]) {
        [self.delegate calendarView:self didSelectRange:self.selectedRange];
    }

}

//监听被点击的DayView
- (WJJCalendarDayView*)dayViewForTouches:(NSSet*)touches {
    if (touches.count != 1) {
        //不支持多点touch
        return nil;
    }
    UITouch *touch = [touches anyObject];
    // 监听点击是否在container内
    if (!CGRectContainsPoint(self.monthContainerView.frame, [touch locationInView:self.monthContainerView.superview])) {
        return nil;
    }
    // Work out which day view was touched. We can't just use hit test on a root view because the month views can overlap
    for (WJJCalendarMonthView *monthView in self.monthViews.allValues)
    {
        UIView *view = [monthView hitTest:[touch locationInView:monthView] withEvent:nil];
        if (view == nil)
        {
            continue;
        }
        while (view != monthView)
        {
            if ([view isKindOfClass:[WJJCalendarDayView class]])
            {
                return (WJJCalendarDayView*)view;
            }
            view = view.superview;
        }
    }
    return nil;
}


@end
